package org.snail.transport.handler;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import org.snail.transport.protocol.Header;
import org.snail.transport.protocol.Message;
import org.snail.transport.protocol.MessageType;

import java.util.List;

/**
 * snail消息头16个字节定长
 *
 * ****************************************************************************************************
 *                                          protocol
 *  ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
 *       2   │   1   │    1   │     8     │      4      │
 *  ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
 *           │       │        │           │             │
 *  │  magic   sign    status   invokeId    body length                   body                       │
 *           │       │        │           │             │
 *  └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
 *
 * ****************************************************************************************************
 * + 2    magic,(short) 0xbabe。
 * + 1    消息标志位低地址4位用来表示消息类型request/response/heartbeat, 高地址4位用来表示序列化类型。
 * + 1    状态位, 设置请求响应状态。
 * + 8    invokeId。
 * + 4    消息体 body 长度, int 类型。
 *
 * 核心解码
 * Created by fish on 17/4/3.
 */
public class SnailDecoder extends ReplayingDecoder<SnailDecoder.State>  {

    private final Header header = new Header();

    public SnailDecoder(){
        super(State.HEADER_MAGIC);
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {

        switch (state()) {
            case HEADER_MAGIC:
                if(header.MAGIC != byteBuf.readShort()){
                   throw new RuntimeException("magic error");
                }
                checkpoint(State.HEADER_SIGN);
            case HEADER_SIGN:
                header.setSign(byteBuf.readByte());
                checkpoint(State.HEADER_STATUS);
            case HEADER_STATUS:
                header.setStatusType(byteBuf.readByte());
                checkpoint(State.HEADER_INVOKE_ID);
            case HEADER_INVOKE_ID:
                header.setInvokeId(byteBuf.readLong());
                checkpoint(State.HEADER_BODY_LENGTH);
            case HEADER_BODY_LENGTH:
                header.bodyLength(byteBuf.readInt());
                checkpoint(State.BODY);
            case BODY:
                switch (header.getMessageType()){
                    case MessageType.HEARTBEAT:
                        break;
                    case MessageType.REQUEST:
                    case MessageType.RESPONSE:
                        byte[] body = new byte[header.bodyLength()];
                        byteBuf.readBytes(body);
                        Message message = new Message(header,body);
                        list.add(message);
                        break;
                    default:
                        throw new RuntimeException("messageType error");
                }
            checkpoint(State.HEADER_MAGIC);
        }

    }


    enum State {
        HEADER_MAGIC,
        HEADER_SIGN,
        HEADER_STATUS,
        HEADER_INVOKE_ID,
        HEADER_BODY_LENGTH,
        BODY
    }
}
